package org.sqlproc.engine.impl;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.apache.commons.beanutils.MethodUtils;
import org.sqlproc.engine.SqlFeature;
import org.sqlproc.engine.impl.type.SqlMetaType;

/**
 * SQL Processor utilities.
 * 
 * @author <a href="mailto:Vladimir.Hudec@gmail.com">Vladimir Hudec</a>
 */
public class SqlUtils {

    static final String SUPPVAL_NOTNULL = "notnull";
    static final String SUPPVAL_ANY = "any";
    static final String SUPPVAL_NULL = "null";
    static final String SUPPVAL_SEQUENCE = "seq";
    static final String SUPPVAL_ID = "id";
    static final String SUPPVAL_GTYPE = "gtype";
    static final String SUPPVAL_TYPE = "type";

    // enums

    public static Object getEnumToValue(Object obj) {
        if (obj == null)
            return null;
        for (String methodName : SqlProcessContext.getFeatures(SqlFeature.METHODS_ENUM_IN)) {
            try {
                return MethodUtils.invokeMethod(obj, methodName, null);
            } catch (NoSuchMethodException e) {
                continue;
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }

    public static Object getValueToEnum(Class<?> objClass, Object val) {
        if (val == null)
            return null;
        for (String methodName : SqlProcessContext.getFeatures(SqlFeature.METHODS_ENUM_OUT)) {
            try {
                return MethodUtils.invokeStaticMethod(objClass, methodName, val);
            } catch (NoSuchMethodException e) {
                continue;
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }
        return null;
    }

    // the emptiness of input values

    public static boolean isEmpty(Object obj, SqlType sqlType, boolean inSqlSetOrInsert)
            throws IllegalArgumentException {
        String value = (sqlType != null && sqlType.getValue() != null) ? sqlType.getValue().toLowerCase() : null;

        if (SUPPVAL_NOTNULL.equalsIgnoreCase(value)) {
            if (obj == null)
                throw new IllegalArgumentException(SUPPVAL_NOTNULL);
        }

        if (inSqlSetOrInsert) {
            if (obj == null)
                return true;
        }

        if (SUPPVAL_ANY.equalsIgnoreCase(value)) {
            return true;
        } else if (SUPPVAL_NULL.equalsIgnoreCase(value)) {
            if (obj == null)
                return true;
            else
                return false;
        } else {
            if (obj == null) {
                return false;
            } else if (obj instanceof Collection<?>) {
                if (((Collection<?>) obj).isEmpty())
                    return false;
            } else if (obj.toString().length() <= 0) {
                return false;
            }
            return true;
        }
    }

    // the true of boolean expressions

    public static boolean isTrue(Object obj, SqlType sqlType) {
        if (sqlType == null || sqlType.getValue() == null) {
            if (obj != null) {
                if (obj instanceof Boolean) {
                    return ((Boolean) obj).booleanValue();
                } else if (obj instanceof String) {
                    String str = ((String) obj).trim();
                    return (str.length() > 0 && !str.equalsIgnoreCase("false"));
                } else if (obj instanceof Number) {
                    return ((Number) obj).longValue() > 0;
                } else if (obj.getClass().isEnum()) {
                    return true;
                } else {
                    return true; // not null
                }
            }
            return false;
        } else {
            if (obj == null) {
                if (sqlType.getValue().toLowerCase().equalsIgnoreCase(SUPPVAL_NULL))
                    return true;
                else
                    return false;
            } else {
                if (obj.getClass().isEnum()) {
                    if (obj.toString().equals(sqlType.getValue())) {
                        return true;
                    } else if (sqlType.getMetaType() == SqlMetaType.ENUM_STRING) {
                        return sqlType.getValue().equals(getEnumToValue(obj));
                    } else if (sqlType.getMetaType() == SqlMetaType.ENUM_INT) {
                        return sqlType.getValue().equals(getEnumToValue(obj).toString());
                    } else
                        return false;
                } else {
                    if (obj.toString().equals(sqlType.getValue()))
                        return true;
                    else
                        return false;
                }
            }
        }
    }

    // miscs

    public static Byte[] toBytes(byte[] barr) {
        if (barr == null)
            return null;
        Byte[] res = new Byte[barr.length];
        for (int i = 0; i < barr.length; i++)
            res[i] = new Byte(barr[i]);
        return res;
    }

    public static byte[] toBytes(Byte[] barr) {
        if (barr == null)
            return null;
        byte[] res = new byte[barr.length];
        for (int i = 0; i < barr.length; i++)
            res[i] = (barr[i] != null) ? barr[i].byteValue() : (byte) 0;
        return res;
    }

    public static String getFeature(Map<String, Object> features, String name) {
        Object o = features.get(name);
        return (o != null && o instanceof String) ? (String) o : null;
    }

    public static Boolean isFeature(Map<String, Object> features, String name, Boolean defaultValue) {
        Object o = features.get(name);
        if (o == null || !(o instanceof Boolean))
            return defaultValue;
        return (Boolean) o;
    }

    public static boolean isEmpty(StringBuilder sb) {
        if (sb == null)
            return true;
        String s = sb.toString().trim();
        if (s.length() == 0)
            return true;
        return false;
    }

    public static boolean[] changedIdentities(Object resultValue[], Object[] previousResultValue) {
        boolean[] changedIdentities = new boolean[resultValue.length];
        if (previousResultValue == null) {
            for (int i = 0; i < resultValue.length; i++) {
                changedIdentities[i] = true;
            }
            return changedIdentities;
        }

        for (int i = 0; i < resultValue.length; i++) {
            Object newIdentity = resultValue[i];
            Object previousIdentity = previousResultValue[i];
            boolean result = false;
            if (previousIdentity != null && newIdentity == null)
                result = true;
            else if (previousIdentity == null && newIdentity != null)
                result = true;
            else if (previousIdentity == null && newIdentity == null)
                result = false;
            else
                result = !previousIdentity.equals(newIdentity);
            changedIdentities[i] = result;
        }
        return changedIdentities;
    }

    public static boolean changedIdentity(boolean[] changedIdentities, Integer identityIndex) {
        if (changedIdentities == null || identityIndex == null)
            return true;
        return changedIdentities[identityIndex];
    }

    public static boolean changedIdentity(boolean[] changedIdentities, List<Integer> identityIndexes) {
        if (changedIdentities == null || identityIndexes == null)
            return true;
        for (Integer identityIndex : identityIndexes) {
            if (changedIdentities[identityIndex])
                return true;
        }
        return false;
    }
}
